#include "zrpc/base/logger.h"
namespace zrpc {
Logger::LogLevel InitLogLevel() {
#ifdef _WIN64
  return Logger::INFO;
#else
  if (::getenv("TRACE"))
    return Logger::TRACE;
  else if (::getenv("DEBUG"))
    return Logger::DEBUG;
  else
    return Logger::INFO;
#endif
}

Logger::LogLevel g_log_level = InitLogLevel();

const char *LogLevelName[Logger::NUM_LOG_LEVELS] = {
    "TRACE ", "DEBUG ", "INFO  ", "WARN  ", "FATAL ",
};

void DefaultOutput(const char *msg, int32_t len) {
  size_t n = ::fwrite(msg, 1, len, stdout);
  // FIXME check n
  (void)n;
}

void DefaultFlush() { fflush(stdout); }

Logger::OutputFunc g_output = DefaultOutput;
Logger::FlushFunc g_flush = DefaultFlush;

Logger::Impl::Impl(LogLevel level, int32_t saved_errno, const SourceFile &file,
                   int32_t line)
    : stream_(),
      level_(level),
      line_(line),
      base_name_(file),
      time_(TimeStamp::NowMicros()) {
  FormatTime();
  stream_ << T(LogLevelName[level], 6);
}

char t_time[64];
time_t t_last_second;

void Logger::Impl::FormatTime() {
  int32_t len = 0;
  int64_t epoch = time_.GetMicroSecondsSinceEpoch();
  time_t seconds =
      static_cast<time_t>(epoch / TimeStamp::kMicroSecondsPerSecond);
  int microseconds =
      static_cast<int>(epoch % TimeStamp::kMicroSecondsPerSecond);
  if (seconds != t_last_second) {
    t_last_second = seconds;
    struct tm tmtime = *(localtime(&seconds));
    len = snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d",
                   tmtime.tm_year + 1900, tmtime.tm_mon + 1, tmtime.tm_mday,
                   tmtime.tm_hour, tmtime.tm_min, tmtime.tm_sec);
    assert(len == 17);
    (void)len;
  }

  stream_ << T(t_time, 17);
  stream_ << "  ";
}

void Logger::Impl::Finish() {
  stream_ << " - " << base_name_.data_ << ':' << line_ << '\n';
}

Logger::Logger(SourceFile file, int32_t line) : impl_(INFO, 0, file, line) {}

Logger::Logger(SourceFile file, int32_t line, LogLevel level, const char *func)
    : impl_(level, 0, file, line) {
  impl_.stream_ << func << ' ';
}

Logger::Logger(SourceFile file, int32_t line, LogLevel level)
    : impl_(level, 0, file, line) {}

Logger::~Logger() {
  impl_.Finish();
  const LogStream::Buffer &buf(Stream().GetBuffer());
  g_output(buf.GetData(), buf.Length());
  if (impl_.level_ == FATAL) {
    g_flush();
    abort();
  }
}

void Logger::SetLogLevel(Logger::LogLevel level) { g_log_level = level; }

void Logger::SetOutput(const OutputFunc &func) { g_output = func; }

void Logger::SetFlush(const FlushFunc &func) { g_flush = func; }
}  // namespace zrpc